/*
 * Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef VIRTNET_LM_H
#define VIRTNET_LM_H

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <infiniband/verbs.h>
#include <infiniband/mlx5dv.h>

#include "virtnet_vq.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * dirty memory map mode
 *
 * @DIRTY_BIT:  each bit represents one dirty page
 * @DIRTY_BYTE: each byte represents one dirty page
 */
enum dirty_bmap_mode {
	DIRTY_BIT,
	DIRTY_BYTE,
};

/**
 * dirty memory bmap memory location
 *
 * @DIRTY_BMAP_REMOTE: bmap memory is allocated on remote side
 * @DIRTY_BMAP_LOCAL:  bmap memory is allocated on local side
 */
enum dirty_bmap_access_flag {
	DIRTY_BMAP_REMOTE,
	DIRTY_BMAP_LOCAL,
};

/**
* dirty memory bmap memory location
*
* @dirty_bmap_addr:  dirty bmap sg memory address,
*                    when flag == DIRTY_BMAP_REMOTE,  physical address in dirty_bmap_addr as input
*                    when flag == DIRTY_BMAP_LOCAL,  virtual address in dirty_bmap_addr as output
* @dirty_bmap_size:  dirty bmap sg memory size
*/
struct dirty_bmap_sge {
      uint64_t	dirty_bmap_addr;
      uint32_t	dirty_bmap_size;
};

/**
 * dirty memory bmap memory location
 *
 * @flag: indicates where the memory(@dirty_bmap_addr) is allocated
 * @mode: indicates dirty page is tracked by each bit or byte
 * @dirty_bmap_addr:  dirty bmap memory address
 * @dirty_bmap_size:  dirty bmap memory address
 * @num_sge        :  number of sges, in case of flag is local, num_sge can only be 1
 *                     dirty_bmap_addr contains virtual address as output
 * @guest_page_size:  page size the dirty bmap is tracking
 */
struct virtnet_migration_log {
	enum dirty_bmap_access_flag	flag;
	enum dirty_bmap_mode		mode;

	uint32_t guest_page_size;

	uint32_t num_sge;
	struct dirty_bmap_sge sge[];
};

/**
 * Freeze device, stop doing DMA and changing its state.
 * Virt queues are suspected
 *
 * @param dev
 *	pointer to device
 * @return
 * 	0 on success, error otherwise
 */
int virtnet_freeze(struct virtnet_device *dev);

/**
 * Unfreeze device, start doing DMA.
 * This recreates the virt queues
 *
 * @param dev
 *	pointer to device
 * @return
 * 	0 on success, error otherwise
 */
int virtnet_unfreeze(struct virtnet_device *dev);

/**
 * Get current live migration state size in byte.
 *
 * @param dev
 *	pointer to device
 * @return
 * 	size in bytes
 */
uint32_t virtnet_get_state_size(struct virtnet_device *dev);

/**
 * Read state from migration source and save to buffer
 *
 * @param dev
 *	pointer to device
 * @param addr
 *	pointer to buffer which saves the state
 * @param size
 *	size of the sate in bytes
 * @return
 * 	0 on success, error otherwise
 */
int virtnet_read_state(struct virtnet_device *dev,
		       uint8_t *addr, uint32_t size);

/**
 * Write state on migration target
 *
 * @param dev
 *	pointer to device
 * @param addr
 *	pointer to buffer which saves the state
 * @param size
 *	size of the sate in bytes
 * @return
 * 	0 on success, error otherwise
 */
int virtnet_write_state(struct virtnet_device *dev,
			uint8_t *addr, uint32_t size);

/**
 * Device starts to track dirty pages and save dirty page info to log
 *
 * @param dev
 *	pointer to device
 * @param log
 *	pointer to log information, caller should fill the info
 * @return
 * 	0 on success, error otherwise
 */
int virtnet_start_dirty_pages_track(struct virtnet_device *dev,
				    struct virtnet_migration_log *log);

/**
 * Device stops tracking dirty pages
 *
 * @param dev
 *	pointer to device
 * @return
 * 	0 on success, error otherwise
 */
int virtnet_stop_dirty_pages_track(struct virtnet_device *dev);

#ifdef __cplusplus
}
#endif

#endif

